import json
import os.path
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Process

from flask import request

from httpserver.json_flask import JsonFlask
from httpserver.json_response import JsonResponse
from tess_local_simulator.main import start_local_tess_simulator
from httpserver.utils import get_device_id
from utils.redis_utils import send_redis_hset, get_redis_hset,delte_redis_set

app = JsonFlask(__name__)
# 添加车辆数
platoon = 0
vehicleNum = 0
vehiclePoints = None
status = None

PENDING = 'pending'
SUCCESS = 'success'
ERROR = 'error'

process_car_dict = {}

process_dist = {}
# 限制仿真最大同时运行进程数量为20
max_process_num = 1
# 限制仿真最大超时等待时间为一小时
max_wait_time = 60 * 60

executor = ThreadPoolExecutor(max_workers=20)


@app.route('/jd/startTessng', methods=['Post'])
def startLocalSimulator():
    '''
        启动仿真
    '''
    global process_dist
    # 获取仿真配置
    sim_cfg = request.get_json()
    print(sim_cfg)
    # assert 'type' in sim_cfg.keys(), '车辆行为不能为空，0-tess托管，3-逐帧映射'
    config_dict = {}
    # config_dict['netPath'] = r'D:\wn\cd_playoff\net_file\长安大学.tess'
    config_dict['netPath'] = r'net_file\长安大学.tess'
    config_dict['proj'] = '+proj=tmerc +lon_0=108.90575652010739 +lat_0=34.37650478465651 +ellps=WGS84'
    # config_dict['type'] = sim_cfg['type']
    config_dict['target_topic'] = 'CA001MatchResultData'
    config_dict['radical_ratio'] = 0
    # 打开文件
    with open(r'net_file\params0716.json', 'r') as file:
        data = json.loads(file.read())
        data = {int(k): v for k, v in data.items()}
        config_dict['scene_dict'] = data
    config_dict['dataChannel'] = sim_cfg['dataChannel']
    config_dict['commandChannel'] = sim_cfg['commandChannel']
    config_dict['statusChannel'] = sim_cfg['statusChannel']
    config_dict['deviceId'] = 4
    # threading.Thread(target=get_device_id, args=(sim_cfg['commandChannel'],)).start()
    # get_device_id(sim_cfg['commandChannel'])
    # config_dict['deviceId'] = int(time.time())
    print('启动仿真', "dataChannel:", config_dict['dataChannel'], "commandChannel", config_dict['commandChannel'])
    for i in range(max_process_num):
        key = sim_cfg['dataChannel']
        if key in process_dist:
            return JsonResponse.success("仿真程序已经在运行", "success")
        if len(process_dist.keys()) > max_process_num:
            return JsonResponse.error("正在运行的仿真程序超过最大进程数，请稍等重试", "error")
        tess_process = Process(target=start_local_tess_simulator, args=(config_dict,))
        tess_process.start()
        process_dist[key] = {"process": tess_process, "startTime": int(time.time())}
    return JsonResponse.success("仿真程序启动成功", "success")
    # return {"status": "success", "code": 200, "msg": "", "data": ""}
    # return JsonResponse.success_with_msg('仿真程序启动成功')


@app.route('/wj/startTessngProcess', methods=['Post'])
def startLocalSimulatorPorcess():
    '''
        启动仿真
    '''
    global process_dist
    # 获取仿真配置
    sim_cfg = request.get_json()
    # print(sim_cfg)
    config_dict = {}
    config_dict['netPath'] = r'net_file\长安大学.tess'
    config_dict['proj'] = '+proj=tmerc +lon_0=108.90575652010739 +lat_0=34.37650478465651 +ellps=WGS84'
    # 打开文件
    with open(r'net_file\params0716.json', 'r') as file:
        data = json.loads(file.read())
        data = {k: v for k, v in data.items()}
        config_dict['scene_dict'] = data
    # 初始化进程群，默认开启max_process_num（20）个进程，等待被域控调用
    delte_redis_set("processInfo")
    for i in range(max_process_num):
        key = str(i)
        config_dict['process_name'] = key
        # key = sim_cfg['dataChannel']
        if len(process_dist.keys()) >= max_process_num:
            return JsonResponse.error("正在运行的仿真程序超过最大进程数，请稍等重试", "error")
        if key in process_dist:
            return JsonResponse.success("仿真程序已经在运行", "success")
        tess_process = Process(target=start_local_tess_simulator, args=(config_dict,))
        tess_process.start()
        process_dist[key] = {"process": tess_process, "startTime": int(time.time())}
        executor.submit(send_redis_hset, "processInfo", str(i),
                        str({"isUsed": 0}))

    return JsonResponse.success("仿真进程群启动成功", "success")


@app.route('/wj/startTessngSingleScence', methods=['Post'])
def startLocalSimulatorSingleScence():
    '''
        启动仿真
    '''
    global process_dist
    # 获取仿真配置
    sim_cfg = request.get_json()
    print(sim_cfg)
    config_dict = {}
    start_process = False
    config_dict['EgoInfoChannel'] = sim_cfg['EgoInfoChannel']
    config_dict['SimulationEgoChannel'] = sim_cfg['SimulationEgoChannel']
    config_dict['ProcessStatusChannel'] = sim_cfg['ProcessStatusChannel']
    config_dict['TaskId'] = sim_cfg['TaskId']
    config_dict['scene'] = sim_cfg['scene']
    config_dict['data_format'] = sim_cfg['data_format']
    # print('启动仿真', "dataChannel:", config_dict['dataChannel'], "commandChannel", config_dict['commandChannel'])
    print('单场景启动仿真', config_dict)
    for i in range(max_process_num):
        processInfo = get_redis_hset("processInfo", str(i))
        # 如果进程在使用中，查询下一个进程是否空闲
        if processInfo['isUsed'] == 1:
            continue
        else:
            start_process = True
            config_dict['isUsed'] = 1
            config_dict['startTime'] = int(time.time())
            config_dict['UsedEnd'] = 0
            config_dict['Using'] = 0
            executor.submit(send_redis_hset, "processInfo", str(i),
                            str(config_dict))
            break
    if start_process:
        return JsonResponse.success("仿真程序启动成功", "success")
    else:
        return JsonResponse.error("仿真程序启动失败，进程群里没有可用的进程", "error")


@app.route('/jd/stopTessng', methods=['Post'])
def stopSimulator():
    '''
        启动仿真
    '''
    global process_dist
    # 获取仿真配置
    sim_cfg = request.get_json()
    key = sim_cfg['dataChannel']
    if key not in process_dist.keys():
        return JsonResponse.success('该测试任务未运行', "success")
    task_process = process_dist.get(key).get("process")
    process_dist.pop(key)
    task_process.terminate()
    task_process.join()
    return JsonResponse.success('仿真停止成功', "success")


@app.route('/jd/getProcessInfo', methods=['Get'])
def get_process_info():
    global process_dist

    now_time = int(time.time())
    process_dist_copy = process_dist.copy()
    for k, v in process_dist_copy.items():
        process_start_time = v["startTime"]
        # print(now_time - process_start_time)
        v['runTime'] = now_time - process_start_time
        v['process'] = k
        process_dist_copy[k] = v
    return {"processNum": len(process_dist_copy.keys()), "processInfo": process_dist_copy}


@app.errorhandler(Exception)
def error_handler(e):
    """
    全局异常捕获
    """
    return JsonResponse.error(msg=str(e))


def runstart():
    app.run(host='0.0.0.0', port=5002)


def listen_process_status():
    """
    监听进程群中的进程状态，若进程启动时间大于设定的最大仿真时间，进程主动关闭并开启新的空白进程等待连接
                       若进程启动时间大于设定的最大仿真时间，进程主动关闭并开启新的空白进程等待连接
    :return:
    """
    global process_dist
    while True:
        now_time = int(time.time())
        for i in range(max_process_num):
            processInfo = get_redis_hset("processInfo", str(i))
            print(i, processInfo)
            if processInfo:
                if 'startTime' in processInfo.keys():
                    startTime = processInfo['startTime']
                    if now_time - startTime >= 60 * 10:
                        if str(i) not in process_dist.keys():
                            return JsonResponse.success('该测试任务未运行', "success")
                        task_process = process_dist.get(str(i)).get("process")
                        process_dist.pop(str(i))
                        task_process.terminate()
                        task_process.join()
                        print("进程" + str(i) + "超过最大仿真时间，主动结束")
                        startLocalSimulatorOnePorcess(str(i))
                if 'UsedEnd' in processInfo.keys():
                    UsedEnd = processInfo['UsedEnd']
                    print("UsedEnd", UsedEnd)
                    if UsedEnd == 1:
                        if str(i) not in process_dist.keys():
                            print('该测试任务未运行', "success")
                        else:
                            task_process = process_dist.get(str(i)).get("process")
                            process_dist.pop(str(i))
                            task_process.terminate()
                            task_process.join()
                            print("进程" + str(i) + "出车超过该场景仿真范围，主动结束")
                            startLocalSimulatorOnePorcess(str(i))
        time.sleep(0.1)


def startLocalSimulatorOnePorcess(key):
    '''
        启动仿真
    '''
    global process_dist

    config_dict = {}
    config_dict['netPath'] = r'net_file\长安大学.tess'
    config_dict['proj'] = '+proj=tmerc +lon_0=108.90575652010739 +lat_0=34.37650478465651 +ellps=WGS84'
    # 打开文件
    with open(r'net_file\params0716.json', 'r') as file:
        data = json.loads(file.read())
        data = {k: v for k, v in data.items()}
        config_dict['scene_dict'] = data
    # 初始化进程群，默认开启max_process_num（20）个进程，等待被域控调用

    config_dict['process_name'] = key
    # key = sim_cfg['dataChannel']
    if len(process_dist.keys()) >= max_process_num:
        return "正在运行的仿真程序超过最大进程数，请稍等重试"
    if key in process_dist:
        return "仿真程序已经在运行"
    tess_process = Process(target=start_local_tess_simulator, args=(config_dict,))
    tess_process.start()
    process_dist[key] = {"process": tess_process, "startTime": int(time.time())}
    executor.submit(send_redis_hset, "processInfo", str(key),
                    str({"isUsed": 0}))
    return "启动成功"


def httpserver():
    # threading.Thread(target=listen_process_status).start()
    runstart()
